﻿using System.Threading;
using System.Net.Util;
using System.Diagnostics;
using System.Threading.Tasks;
namespace System.Net
{
    public abstract class HttpListenerWorkerRequest
    {
        private HttpListenerContext _context;
        private DateTime _startTime;

        public HttpListenerWorkerRequest(HttpListenerContext context)
        {
            _context = context;
            //
            _timeoutStartTime = DateTime.MinValue;
        }

        internal HttpListenerContext Context
        {
            get { return _context; }
        }

        internal virtual DateTime GetStartTime()
        {
            return _startTime;
        }

        internal virtual void ResetStartTime()
        {
            _startTime = DateTime.UtcNow;
        }

        public virtual bool IsClientConnected()
        {
            return true;
        }

        public virtual string GetRemoteAddress()
        {
            return _context.Request.RemoteEndPoint.ToString();
        }

        public virtual string GetLocalAddress()
        {
            return _context.Request.LocalEndPoint.ToString();
        }

        #region Cancellable

        private DateTime _timeoutStartTime;
        private int _timeoutState;
        private DoubleLink _timeoutLink;
        private bool _timeoutSet;
        private TimeSpan _timeout;
        private Thread _thread;
        private Task _task;
        private CancellationToken _cancellationToken;

        internal void InvokeCancellableCallback(WaitCallback callback, object state)
        {
            if (IsInCancellablePeriod)
                callback(state);
            else
            {
                try
                {
                    BeginCancellablePeriod();
                    try { callback(state); }
                    finally { EndCancellablePeriod(); }
                    WaitForExceptionIfCancelled();
                }
                catch (ThreadAbortException ex)
                {
                    if (HttpListenerHost.UseThreading)
                    {
                        if (((ex.ExceptionState != null) && (ex.ExceptionState is HttpListenerHost.CancelModuleException)) && ((HttpListenerHost.CancelModuleException)ex.ExceptionState).Timeout)
                        {
                            Thread.ResetAbort();
                            throw new HttpListenerException(0xbb9, "Request_timed_out");
                        }
                    }
                    else
                        throw;
                }
            }
        }

        internal bool IsInCancellablePeriod
        {
            get { return (_timeoutState == 1); }
        }

        internal void EnsureTimeout()
        {
            if (!_timeoutSet)
            {
                int totalSeconds = (int)HttpListenerHost.ExecutionTimeout.TotalSeconds;
                _timeout = new TimeSpan(0, 0, totalSeconds);
                _timeoutSet = true;
            }
        }

        internal DoubleLink TimeoutLink
        {
            get { return _timeoutLink; }
            set { _timeoutLink = value; }
        }

        internal TimeSpan Timeout
        {
            get
            {
                EnsureTimeout();
                return _timeout;
            }
            set
            {
                _timeout = value;
                _timeoutSet = true;
            }
        }

        internal Thread MustTimeout(DateTime utcNow)
        {
            if ((_timeoutState == 1) && (TimeSpan.Compare(utcNow.Subtract(_timeoutStartTime), Timeout) >= 0))
            {
                try
                {
                    if (HttpListenerHost.IsDebuggingEnabled || Debugger.IsAttached)
                        return null;
                }
                catch { return null; }
                if (Interlocked.CompareExchange(ref _timeoutState, -1, 1) == 1)
                    return _thread;
            }
            return null;
        }

        internal Task MustTimeoutTask(DateTime utcNow)
        {
            if ((_timeoutState == 1) && (TimeSpan.Compare(utcNow.Subtract(_timeoutStartTime), Timeout) >= 0))
            {
                try
                {
                    if (HttpListenerHost.IsDebuggingEnabled || Debugger.IsAttached)
                        return null;
                }
                catch { return null; }
                if (Interlocked.CompareExchange(ref _timeoutState, -1, 1) == 1)
                    return _task;
            }
            return null;
        }

        internal void SetStartTime()
        {
            _timeoutStartTime = DateTime.UtcNow;
        }

        internal void BeginCancellablePeriod()
        {
            if (_timeoutStartTime == DateTime.MinValue)
                SetStartTime();
            _timeoutState = 1;
        }

        internal void EndCancellablePeriod() { Interlocked.CompareExchange(ref _timeoutState, 0, 1); }

        internal void WaitForExceptionIfCancelled()
        {
            while (_timeoutState == -1)
                Thread.Sleep(100);
        }

        #endregion
    }
}
